package cn.jdemo.pattern.singleton;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class Test {

    public static void main(String[] args) throws Exception {
        test08();
    }

    /**
     * // java.lang.NoSuchMethodException
     * // 枚举类中constructor中没有无参构造器只有一个参数为（String.class,int.class）构造器 ，继承自Enum枚举抽象类
     * @see Demo03
     */
    public static void test01() throws Exception {
        Demo03 instance = Demo03.INSTANCE;
        Class<Demo03> demo03Class = Demo03.class;
        Demo03 demo03 = demo03Class.newInstance();// java.lang.NoSuchMethodException
        System.out.println(demo03 == instance);
    }

    /**
     * // java.lang.IllegalArgumentException: Cannot reflectively create enum objects
     * @see Demo03
     * @see java.lang.reflect.Constructor#newInstance() (clazz.getModifiers() & Modifier.ENUM) != 0
     */
    public static void test01Vo() throws Exception {
        Demo03 instance = Demo03.INSTANCE;
        Class<Demo03> demo03Class = Demo03.class;
        Constructor<Demo03> declaredConstructor
                = demo03Class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        Demo03 demo03 = declaredConstructor.newInstance();//  java.lang.IllegalArgumentException: Cannot reflectively create enum objects
        System.out.println(demo03 == instance);
    }

    /**
     * enum 反序列化相同
     * @see Demo03
     */
    public static void test01Vo2() throws Exception {
        Demo03 instance = Demo03.INSTANCE;

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Demo03 serialize = (Demo03) ois.readObject();
        System.out.println(instance == serialize);// true
    }

    /**
     * 报错: java.lang.IllegalAccessException:
     *  Test can not access a member of class Demo04 with modifiers "private"
     *
     * @see Demo04
     */
    public static void test02() throws Exception {
        Demo04 instance = Demo04.getInstance();
        Class<Demo04> demo04Class = Demo04.class;
        Demo04 demo04 = demo04Class.newInstance(); // java.lang.IllegalAccessException: Test can not access a member of class Demo04 with modifiers "private"
        System.out.println(demo04 == instance);
    }

    /**
     * 暴力反射，仍然可以实例私有构造器
     * @see Demo04
     */
    public static void test03() throws Exception {
        Demo04 instance = Demo04.getInstance();
        Class<Demo04> demo04Class = Demo04.class;
        Constructor<Demo04> declaredConstructor = demo04Class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo04 demo04 = declaredConstructor.newInstance();
        System.out.println(demo04 == instance);// false
    }

    /**
     * 输出
     * java.lang.Exception: 禁止实例化！
     * @throws Exception
     * @see Demo04Vo
     */
    public static void test04() throws Exception {
        Demo04Vo instance = Demo04Vo.getInstance();
        Class<Demo04Vo> demo04VoClass = Demo04Vo.class;
        Constructor<Demo04Vo> declaredConstructor = demo04VoClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo04Vo demo04Vo = declaredConstructor.newInstance(); // java.lang.Exception: 禁止实例化！
        System.out.println(demo04Vo == instance);
    }

    /**
     * 注意:
     *   若反射在懒汉加载前,则可以被反射无数次
     *
     * @throws Exception
     * @see Demo04Vo
     */
    public static void test05() throws Exception {
        Class<Demo04Vo> demo04VoClass = Demo04Vo.class;
        Constructor<Demo04Vo> declaredConstructor = demo04VoClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo04Vo demo04Vo = declaredConstructor.newInstance();
        Demo04Vo demo04Vo2 = declaredConstructor.newInstance();
        System.out.println(demo04Vo == demo04Vo2);// false
    }

    /**
     * java.lang.RuntimeException: 禁止实例化！
     * 禁止反射
     * @see Demo05
     */
    public static void test06() throws Exception {
        Class<Demo05> demo05Class = Demo05.class;
        Constructor<Demo05> declaredConstructor = demo05Class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo05 demo05 = declaredConstructor.newInstance();// java.lang.RuntimeException: 禁止实例化！
        Demo05 demo05_02 = declaredConstructor.newInstance();
        System.out.println(demo05 == demo05_02);
    }

    /**
     * 防止克隆
     */
    public static void test07() throws Exception {
        Demo06 instance = Demo06.getInstance();
        Demo06 clone = (Demo06) instance.clone();
        System.out.println(instance == clone);// true
    }

    /**
     * 没有避免反序列化
     * @see Demo07
     */
    public static void test08() throws Exception {
        Demo07 instance = Demo07.instance;

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Demo07 serialize = (Demo07) ois.readObject();

        System.out.println(instance == serialize);// false
        System.out.println(instance == serialize.instance);// true
    }
}
